/**  @file dc.cpp
 *         A drawing client erea for a widget
 *         rect: the client rectangle for drawing
 *         idleHeader: ereas cut from other widgets
 */

#include "dc.h"
#include "stdlib.h"
#include "gdi.h"
#include "stdio.h"
#include "widget.h"
#include "stdint.h"

DC::DC(HWND owner)
{
    header =0;
    idleHeader =0;
    hWnd =owner;
    rect =&owner->rect;
}


DC::~DC()
{
    RectList *p;

#ifdef DEBUG_DC
printf("window %s delete dc: now begin\n", hWnd->name, p);
#endif
    for(p=header; p;)
    {
        p =header;
        header =p->next;
        delete p;
    }

    for(p=idleHeader; p;)
    {
        p =idleHeader;
        idleHeader =p->next;
#ifdef DEBUG_DC
printf("window %s delete dc: list: %x\n", hWnd->name, p);
#endif
        delete p;
    }
}

void DC::operator delete(void *t)
{
    port_free(t);
}

void *DC::operator new(unsigned int len)
{
    return port_malloc(len);
}

//check if a point is in rect
int DC::isInRect(short x, short y)
{
    return (rect->x1 > x) && (rect->x0 <= x) && (rect->y1 > y) && (rect->y0 <= y);
}

int DC::isInRect(short x, short y, Rect *r)
{
    return (r->x1>x) && (r->x0<=x) && (r->y1>y) && (r->y0<=y);
}

//check if a new Rect overlap
int DC::isOverlap(const Rect *pNewRect)
{
    int t;
    t= (pNewRect->x0 >= rect->x1) || (pNewRect->y0 >= rect->y1) || (pNewRect->x1 <= rect->x0) || (pNewRect->y1 <= rect->y0) ;
    return !t;
}

/** @note cut out the overlapped rectangle
 */
int DC::intersectRect(Rect *intersectedRect, const Rect *pNewRect)
{
    intersectedRect->x0 = max(pNewRect->x0, rect->x0);
    intersectedRect->y0 = max(pNewRect->y0, rect->y0);
    intersectedRect->x1 = min(pNewRect->x1, rect->x1);
    intersectedRect->y1 = min(pNewRect->y1, rect->y1);

    return 1;
}

int DC::isOverCover(const Rect *pRect)
{
     return  (pRect->x0 >= rect->x0) && (pRect->y0 >= rect->y0) &&(pRect->x1 <= rect->x1) &&(pRect->y1 <= rect->y1);
}

int DC::isZeroSize(const Rect *pRect)
{
    return (pRect->x0 > pRect->x1) || (pRect->y0 >pRect->y1);
}


void DC::cleanIdleRect()
{
    RectList *t;

#ifdef DEBUG_DC
    printf("window %s release Rect:\n", hWnd->name);
#endif

    //release all idle ereas;
    for(idleHeader; idleHeader;)
    {
#ifdef DEBUG_DC
        printf("    idleRect chain rect: %x, window: %s, rect: %d.%d %d.%d\n", idleHeader->rect, idleHeader->hWnd->name, idleHeader->rect->x0,idleHeader->rect->y0, idleHeader->rect->x1,idleHeader->rect->y1);
#endif
        idleHeader =idleHeader->removeRect(idleHeader->rect);
    }
    
}

/**
 * @note remove a rect from rectangle
 */
int DC::cutRect(Rect *r, HWND hWindow)
{
    if(isZeroSize(rect))
    {
        return 0;
    }
    if(isZeroSize(r))
    {
        return 0;
    }
    
    if(isOverlap(r))
    {
        if(idleHeader->findRect(r))
        {
            return 0;
        }
        
        idleHeader =idleHeader->addRect(r, hWindow);

        #ifdef DEBUG_DC
        {
            RectList *t_list;
            printf("window %s add an idleRect: \n", hWnd->name);
            for(t_list=idleHeader; t_list; t_list=t_list->next)
            {
                printf("    idleRect chain rect: %x, window: %s rect:%d.%d %d.%d\n", t_list->rect, t_list->hWnd->name, t_list->rect->x0,t_list->rect->y0, t_list->rect->x1,t_list->rect->y1);
            }
        }
        #endif
        return 1;
    }

    return 0;
}

/**
 * @note add a rect to DC
 */
int DC::pasteRect(Rect *r)
{
    //if this rect is in idleRect, remove it;
    if(idleHeader->findRect(r))
    {
        idleHeader =idleHeader->removeRect(r);
        #ifdef DEBUG_DC
        printf("window %s gained one  rect: %d.%d  %d.%d\n", hWnd->name, r->x0,r->y0, r->x1, r->y1);
        #endif
        #ifdef DEBUG_DC
        {
            RectList *t_list;
            for(t_list=idleHeader; t_list; t_list=t_list->next)
            {
                printf("    now idleRect chain rect: %x, chain: %x, window %s\n",  t_list->rect, t_list, t_list->hWnd->name);
            }
        }
        #endif
        return 1;
    }
    return 0;
}

#if 0
/** 
 * @berief clean up all clip rects;
 */
void DC::releaseClients()
{
    ClipRect *pCR;

    pCR = header;
    while(pCR)
    {
        header =header->next;
        delete pCR;
        pCR =header;
    }

    pCR = idleHeader;
    while(pCR)
    {
        idleHeader =idleHeader->next;
        delete pCR;
        pCR =idleHeader;
    }
    return ;
}


/**
 * @brief Client erea be reset to a rectangle
 */
int DC::setRect(Rect *rect)
{
    releaseClients();

    if((rect->x0>rect->x1) || (rect->y0>rect->y1) )
    {
        header =0;
        return 0;
    }
    
    header = new ClipRect;

    return 1;
}

/**
 * @brief add a chain of objects  to the end of list
 */
ClipRect * DC::listInsertTail(ClipRect *list, ClipRect *item)
{
    ClipRect *tcr;

    if(!list) return list;
    if(!item) return list;

    tcr =list;

    while(list->next) list=list->next;
    list->next =item;
    item->prev =item;

    return tcr;
}

/**
 * @brief insert a chain of objects before current position
 */
ClipRect * DC::listInsertBefore(ClipRect * list, ClipRect * item)
{
    ClipRect *tcr;
    
    if(!list) return list;
    if(!item) return list;

    tcr =item;

    item->prev =list->prev;
    if(list->prev)
        list->prev->next =item;
    
    while(item->next)
    {
        item=item->next;
    }
    item->next =list;
    list->prev =item;

    return tcr;
}

/**
 * @brief Remove one item from list
 */
ClipRect * DC::listRemove(ClipRect *list, ClipRect *item)
{
    ClipRect *head;

    head =list;

    if(list ==item)
    {
        head =list->next;

        //link list remove this item
        if(item->next)
        {
            item->next->prev =item->prev;
        }
        if(item->prev)
        {
            item->prev ->next =item->next;
        }

        item->next=item->prev =0;
        return head;
    }

    while(list->next)
    {
        if(list->next == item)
        {
            list->next =item->next;
            if(item->next)
            {
                item->next->prev =list;
            }
            break;
        }
        list =list->next;
    }

    return head;
}

/**
 * @brief insert a chain of objects behind current position
 */
ClipRect * DC::listInsertBehind(ClipRect *list, ClipRect *item)
{
    ClipRect *head=list;
    ClipRect *t;

    if(list) return list;
    if(item) return list;

    //find the last item
    t=item;
    while(t->next) t= t->next;

    //insert to list chain
    t->next =list->next;
    if(list->next)
    {
        list->next->prev =t;
    }
    
    list->next =item;
    item->prev =list;

    return head;
}

//client erea substruct a list of rectangles
int DC::removeRect(ClipRect *clips)
{
    ClipRect *t_clip, *t_current;
    ClipRect *t_cr;

    //for all clips
    for(t_clip =clips; t_clip; t_clip=t_clip->next)
    {
        //for all client regions    
        t_current =header;
        for(;t_current;)
        {
            t_cr=t_current-> removeRect(&t_clip->rect);

             //un clipped
            if(t_cr==t_current)
            {
                t_current =t_current->next;
            }

            //partially cut, a region changed into a list of regions
            else if(t_cr)
            {
                ClipRect *t;
                t =t_current;
                t_current =t_current->next;
                
                listInsertBehind(t, t_cr);
                listRemove(t, t);
                
                if(t ==header)
                {
                    header = t_cr;
                }
                delete t;
            }
             
             //over covered
            else
            {
                ClipRect *t;
                t =t_current;
                t_current =t_current->next;

                listRemove(t,t);
                if(t==header)
                {
                    header =t_current;
                }

                delete t;
            }
        }

    }
    return 1;
}
#endif

int DC::drawPoint(short x0, short y0, char value)
{
    RectList *t;

    if(isInRect(x0,y0))
    {
       for(t=idleHeader; t; t=t->next)
       {
           if(isInRect(x0,y0, t->rect))
           {
               return 0;
           }
        }

        pGdi->drawPoint(x0,y0,value);
        return 1;
    }
    return 0;
}

int DC::reversePoint(short x0, short y0)
{
    RectList *t;

    if(isInRect(x0,y0))
    {
       for(t=idleHeader; t; t=t->next)
       {
           if(isInRect(x0,y0, t->rect))
           {
               return 0;
           }
        }

        pGdi->reversePoint(x0,y0);
        return 1;
    }

    return 0;
}

int DC::fillRect(short x0, short y0, short x1, short y1, char value)
{
    short x,y;
    for(x=x0; x<x1; x++)
    {
        for(y=y0; y<y1; y++)
        {
            drawPoint(x,y,value);
        }
    }

    //dirty(x0,y0, x1,y1);
    return 0;
}

int DC::drawRect(short x0, short y0, short x1, short y1, char value)
{
    short x,y;
    for(x=x0; x<x1;x++)
    {
        for(y=y0; y<y1;y++)
        {
            if( (x==x0) || (y==y0) || (x==x1-1) || (y==y1-1))
            {
                drawPoint(x,y,value);
            }else{
                //drawPoint(x,y,0);
            }
        }
    }

    //dirty(x0,y0, x1,y1);
    return 0;
}

int DC::reverseRect(short x0, short y0, short x1, short y1)
{
    short x,y;
    for(x=x0 ; x<x1; x++)
    {
        for(y=y0; y<y1; y++)
        {
            reversePoint(x,y);
        }
    }
    
    //dirty(x0,y0, x1,y1);
    return 0;
}

int DC::drawFont(short x0, short y0, char *pchar, char valueB,char valueW, FONT* font)
{
    char t_char;
    short t_y, t_x;
    
    char *pCharFont;
    pCharFont =FONT_ADDR(*pchar);

    for(  t_y=y0; t_y<y0+font->height; t_y+=8)
    {
       short y;
       t_x =x0;

        //draw first row of blank
        for(y=t_y; y<y0+font->height; y++)
        {
            drawPoint(t_x,y,valueW);
        }

        //draw a font
        t_x++;
        for( ; t_x<x0+font->width+1; t_x++)
        {
            t_char =*pCharFont++;
            for( y=t_y; y<y0+font->height; y++)
            {
                drawPoint(t_x,y,t_char &1?valueB:valueW);
                t_char >>=1;
            }
        }
    }

    //dirty(x0,y0, t_x, t_y);
    return 0;
}

int DC::drawLine(short x0, short y0, short x1, short y1, char value)
{
    return 0;
}

int DC::drawCircle(short x0, short y0, short x1, short y1, float cosa, char value)
{
    return 0;
}

int DC::drawString(short x0, short y0, short x1, short y1, char *str, char valueB,char valueW, FONT* font)
{
    int t_int;
    char *pchar;

    pchar =str;
    t_int =0;
    while(*pchar){
        if( ( y0+font->width) >=y1) break;
        drawFont( x0+t_int, y0, pchar, valueB,valueW, font);
        t_int += font->width +1;
        pchar ++;
    }

    //dirty(x0,y0, x1,y1);
    return 0;
}

int DC::drawBmp(short x0, short y0, short x1, short y1, char *pbmp)
{
    short t_x,t_y;
    char *pchar =pbmp;
    char t_char;

    for( t_y=y0; t_y<y1; t_y++)
    {
        for( t_x=x0; t_x<x1; t_x++)
        {
            t_char =*pchar++;

            drawPoint(t_x,t_y,t_char);

        }
    }

    //dirty(x0, y0, x1,y1);
    return 0;
}

int DC::dirty(short x0, short y0, short x1, short y1)
{
    if(!pGdi) return -1;
    
    pGdi->dirty(x0, y0,  x1, y1);

    return 0;
}





